#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import json
import re
import requests
import sys
from typing import List, Dict, Optional
from dataclasses import dataclass


@dataclass
class Command:
    """命令结构体"""
    text: str
    description: str


def clean_device_id(device_id: str) -> Optional[str]:
    """
    清理设备ID：删除前导0、空白符、双引号、单引号
    """
    if not device_id:
        return None
    
    # 删除空白符、双引号、单引号
    cleaned = device_id.strip().replace('"', '').replace("'", '')
    
    # 删除前导0，但保留至少一个字符
    cleaned = cleaned.lstrip('0') or '0'
    
    return cleaned if cleaned else None


def load_device_ids(file_path: str) -> List[str]:
    """
    从文件中加载设备ID列表
    """
    device_ids = []
    
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            for line in f:
                line = line.strip()
                if not line:  # 跳过空行
                    continue
                
                cleaned_id = clean_device_id(line)
                if cleaned_id:
                    device_ids.append(cleaned_id)
    
    except FileNotFoundError:
        print(f"错误: 找不到设备ID文件 {file_path}")
        sys.exit(1)
    except Exception as e:
        print(f"错误: 读取设备ID文件失败 {e}")
        sys.exit(1)
    
    return device_ids


def load_commands(file_path: str) -> List[Command]:
    """
    从文件中加载命令列表
    """
    commands = []
    
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            for line in f:
                line = line.strip()
                if not line:  # 跳过空行
                    continue
                
                # 分割命令和描述（以多个空格分隔）
                parts = re.split(r'\s{2,}', line, 1)
                text = parts[0].strip()
                description = parts[1].strip() if len(parts) > 1 else ""
                
                commands.append(Command(text=text, description=description))
    
    except FileNotFoundError:
        print(f"错误: 找不到命令文件 {file_path}")
        sys.exit(1)
    except Exception as e:
        print(f"错误: 读取命令文件失败 {e}")
        sys.exit(1)
    
    return commands


def send_text_to_device(host: str, token: str, device_id: str, text: str) -> Dict:
    """
    向设备发送文本命令
    """
    url = f"http://{host}/api/device/text/send"
    
    headers = {
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {token}'
    }
    
    payload = {
        "device": device_id,
        "is_display": False,
        "urgent": True,
        "text": text
    }
    
    try:
        response = requests.post(url, json=payload, headers=headers, timeout=30)
        
        result = {
            'status_code': response.status_code,
            'success': response.status_code == 200
        }
        
        if response.status_code != 200:
            try:
                error_data = response.json()
                result['error'] = error_data
            except:
                result['error'] = {
                    'code': response.status_code,
                    'message': f'HTTP {response.status_code}: {response.text}'
                }
        
        return result
        
    except requests.exceptions.RequestException as e:
        return {
            'status_code': 0,
            'success': False,
            'error': {
                'code': 500,
                'message': f'请求失败: {str(e)}'
            }
        }


def write_failed_device_to_log(log_file_path: str, device_id: str):
    """
    将失败的设备ID写入日志文件
    """
    try:
        with open(log_file_path, 'a', encoding='utf-8') as f:
            f.write(f"{device_id}\n")
    except Exception as e:
        print(f"警告: 无法写入失败日志文件 {log_file_path}: {e}")


def main():
    parser = argparse.ArgumentParser(description='批量向设备发送文本命令')
    parser.add_argument('-ids', '--device-ids', default='.out.ids',
                        help='设备ID文件路径 (默认: .out.ids)')
    parser.add_argument('-cmd', '--command', type=int, default=1,
                        help='命令索引，从1开始 (默认: 1)')
    parser.add_argument('-host', '--host', default='padstone39.dossp.com:30102', 
                        help='API服务器地址')
    parser.add_argument('-token', '--url-token', default='pawd_padstone',
                        help='API访问令牌 (默认: pawd_padstone)')
    parser.add_argument('-cmd-file', '--command-file', default='cmd_list.txt',
                        help='命令文件路径 (默认: cmd_list.txt)')
    parser.add_argument('-fail-log', '--failed-log', default='failed_devices.log',
                        help='失败设备日志文件路径 (默认: failed_devices.log)')
    
    args = parser.parse_args()
    
    # 加载设备ID列表
    print(f"正在加载设备ID列表: {args.device_ids}")
    device_ids = load_device_ids(args.device_ids)
    print(f"已加载 {len(device_ids)} 个设备ID")
    
    # 加载命令列表
    print(f"正在加载命令列表: {args.command_file}")
    commands = load_commands(args.command_file)
    print(f"已加载 {len(commands)} 个命令")
    
    # 验证命令索引
    if args.command < 1 or args.command > len(commands):
        print(f"错误: 命令索引 {args.command} 超出范围 (1-{len(commands)})")
        sys.exit(1)
    
    # 获取要发送的命令（索引从1开始，所以减1）
    selected_command = commands[args.command - 1]
    
    print(f"\n要发送的命令:")
    print(f"  文本: {selected_command.text}")
    print(f"  描述: {selected_command.description}")
    print(f"  目标主机: {args.host}")
    print(f"  设备数量: {len(device_ids)}")
    print(f"  失败日志: {args.failed_log}")
    print(f"\n开始发送...\n")
    
    # 统计结果
    success_count = 0
    failed_count = 0
    failed_devices = []
    
    # 清空失败日志文件（如果存在）
    try:
        with open(args.failed_log, 'w', encoding='utf-8') as f:
            pass  # 创建空文件
    except Exception as e:
        print(f"警告: 无法创建失败日志文件 {args.failed_log}: {e}")
    
    # 串行执行发送
    for i, device_id in enumerate(device_ids, 1):
        print(f"[{i}/{len(device_ids)}] 设备: {device_id} -> http://{args.host}/api/device/text/send")
        
        result = send_text_to_device(args.host, args.url_token, device_id, selected_command.text)
        
        if result['success']:
            print(f"  ✓ 发送成功")
            success_count += 1
        else:
            print(f"  ✗ 发送失败")
            if 'error' in result:
                error = result['error']
                print(f"    错误代码: {error.get('code', 'unknown')}")
                print(f"    错误消息: {error.get('message', 'unknown error')}")
            failed_count += 1
            failed_devices.append(device_id)
            # 将失败的设备ID写入日志文件
            write_failed_device_to_log(args.failed_log, device_id)
        
        print()  # 空行分隔
    
    # 输出总结
    print("=" * 50)
    print(f"发送完成!")
    print(f"成功: {success_count} 个设备")
    print(f"失败: {failed_count} 个设备")
    print(f"总计: {len(device_ids)} 个设备")
    
    if failed_count > 0:
        print(f"失败设备已记录到: {args.failed_log}")


if __name__ == '__main__':
    main()